package cn.iocoder.yudao.framework.business.eligibility.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.business.audit.enums.AuditStatusEnum;
import cn.iocoder.yudao.framework.business.audit.pojo.form.AuditSaveDTO;
import cn.iocoder.yudao.framework.business.basic.dao.mapper.CoreMapper;
import cn.iocoder.yudao.framework.business.basic.dao.query.CoreQueryWrapper;
import cn.iocoder.yudao.framework.business.basic.service.impl.BaseService;
import cn.iocoder.yudao.framework.business.eligibility.dao.EligibilityApplyMapper;
import cn.iocoder.yudao.framework.business.eligibility.enums.EligibilityCheckStepEnum;
import cn.iocoder.yudao.framework.business.eligibility.pojo.form.EligibilityApplyQueryDTO;
import cn.iocoder.yudao.framework.business.eligibility.pojo.form.EligibilityApplySaveDTO;
import cn.iocoder.yudao.framework.business.eligibility.pojo.po.EligibilityApplyEntity;
import cn.iocoder.yudao.framework.business.eligibility.pojo.vo.EligibilityApplyCheckVO;
import cn.iocoder.yudao.framework.business.eligibility.pojo.vo.EligibilityApplyVO;
import cn.iocoder.yudao.framework.common.biz.config.ConfigCommonApi;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.exception.Assertions;
import cn.iocoder.yudao.framework.common.exception.KnownServiceException;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.constant.ErrorCodeConstants;
import cn.iocoder.yudao.framework.util.TransactionUtils;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Objects;

import static cn.iocoder.yudao.framework.constant.ConfigConstants.CONFIG_MEMBER_AUDIT_TIPS;

/**
 * 用户身份资格申请 Service 实现类
 *
 * @author 山野羡民
 */
//@org.springframework.stereotype.Service
@Slf4j
public abstract class EligibilityApplyBaseService<DO extends EligibilityApplyEntity, RESP extends EligibilityApplyVO, SAVE extends EligibilityApplySaveDTO, QUERY extends EligibilityApplyQueryDTO> extends BaseService<DO, RESP, QUERY> implements EligibilityApplyService<DO, RESP, SAVE, QUERY> {
    @Resource
    private ConfigCommonApi configApi;

    @Nonnull
    @Override
    protected CoreQueryWrapper<DO> buildQueryWrapper(QUERY param) {
        CoreQueryWrapper<DO> wrapper = super.buildQueryWrapper(param);
        if (param == null) {
            return wrapper;
        }
        wrapper.likeIfPresent("contact_name", param.getContactName());
        wrapper.likeIfPresent("contact_mobile", param.getContactMobile());
        wrapper.eqIfPresent("audit_status", param.getAuditStatus());
        wrapper.orderByDesc("id");
        return wrapper;
    }

    @Override
    public DO validateExists(Long id, Integer userType, Long userId) {
        CoreMapper<DO> mapper = getInjectedMapper();
        if (mapper instanceof EligibilityApplyMapper<DO> eligibilityApplyBaseMapper) {
            DO aDo = eligibilityApplyBaseMapper.selectByUserIdAndUserType(userId, userType);
            Assertions.notNull(aDo, "用户申请资料不存在");
            return aDo;
        }
        throw new KnownServiceException("Mapper 必须是" + EligibilityApplyMapper.class.getSimpleName() + " 的子类");
    }

    @Override
    public RESP getDetail(Long id) {
        DO aDo = getInjectedMapper().selectById(id);
        return convertToViewObject(aDo);
    }

    @Nullable
    @Override
    public DO getDetailByUserId(Long userId, @Nullable Integer userType) {
        CoreMapper<DO> mapper = getInjectedMapper();
        if (mapper instanceof EligibilityApplyMapper<DO> eligibilityApplyBaseMapper) {
            return eligibilityApplyBaseMapper.selectByUserIdAndUserType(userId, userType);
        }
        throw new KnownServiceException("Mapper 必须是" + EligibilityApplyMapper.class.getSimpleName() + " 的子类");
    }

    @Override
    public PageResult<RESP> getPage(Integer current, Integer size, @Nonnull QUERY param) {
        PageResult<DO> page = getInjectedMapper().selectPage(current, size, buildQueryWrapper(param));
        if (CollUtil.isEmpty(page.getList())) {
            return PageResult.empty();
        }
        return new PageResult<>(page.getList().stream().map(this::convertToViewObject).toList(), page.getTotal());
    }

    @Override
    public void validateCanCreate(Long userId, @Nullable Integer userType, SAVE reqVO) {
        CoreMapper<DO> mapper = getInjectedMapper();
        if (mapper instanceof EligibilityApplyMapper<DO> eligibilityApplyBaseMapper) {
            int auditPassCount = eligibilityApplyBaseMapper.selectAuditPassCount(userId, userType);
            if (auditPassCount > 0) {
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.AUDIT_PASS_CANNOT_CREATE);
            }
            return;
        }
        throw new KnownServiceException("Mapper 必须是" + EligibilityApplyMapper.class.getSimpleName() + " 的子类");
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long create(Long userId, @Nullable Integer userType, SAVE reqVO) {
        validateCanCreate(userId, userType, reqVO);
        DO aDo = buildSaveInfo(reqVO);
        if (userType == null || Objects.equals(userType, UserTypeEnum.MEMBER.getValue())) {
            aDo.setUserId(userId);
        }
        aDo.setId(null);
        aDo.setAuditStatus(AuditStatusEnum.WAIT.getValue());
        aDo.setAuditNote("");
        getInjectedMapper().insert(aDo);
        TransactionUtils.afterCommit(() -> {
            afterCreatedCommit(reqVO, aDo);
            return null;
        });
        return aDo.getId();
    }

    @SuppressWarnings("unused")
    protected void afterCreatedCommit(SAVE reqVO, DO aDo) {

    }

    @Override
    public void validateCanUpdate(Long id, SAVE reqVO) {
        DO aDo = validateExists(id);
        if (Objects.equals(aDo.getAuditStatus(), AuditStatusEnum.PASS.getValue())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.AUDIT_PASS_CANNOT_UPDATE);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void update(Long id, SAVE reqVO) {
        validateCanUpdate(id, reqVO);
        DO aDo = buildSaveInfo(reqVO);
        aDo.setId(id);
        aDo.setAuditStatus(AuditStatusEnum.WAIT.getValue());
        aDo.setAuditNote("");
        getInjectedMapper().updateById(aDo);
    }

    @Override
    public EligibilityApplyCheckVO<RESP> check(Long userId, @Nullable Integer userType) {
        EligibilityApplyCheckVO<RESP> respVO = new EligibilityApplyCheckVO<>();
        DO aDo = getDetailByUserId(userId, userType);
        if (aDo == null) {
            respVO.setStep(EligibilityCheckStepEnum.NO_DATA.getValue());
            respVO.setTips("还未填写申请资料！");
            return respVO;
        }
        RESP info = buildApplyInfo(aDo);
        respVO.setInfo(info);
        if (Objects.equals(aDo.getAuditStatus(), AuditStatusEnum.WAIT.getValue())) {
            respVO.setStep(EligibilityCheckStepEnum.AUDIT_WAIT.getValue());
            String tips = configApi.getConfigValueByKey(CONFIG_MEMBER_AUDIT_TIPS);
            if (StrUtil.isBlank(tips)) {
                tips = "资料审核中，敬请耐心等待，谢谢！";
            }
            respVO.setTips(tips);
            return respVO;
        }
        if (Objects.equals(aDo.getAuditStatus(), AuditStatusEnum.REJECT.getValue())) {
            respVO.setStep(EligibilityCheckStepEnum.AUDIT_REJECT.getValue());
            respVO.setTips("被驳回，原因：" + aDo.getAuditNote());
            return respVO;
        }
        respVO.setStep(EligibilityCheckStepEnum.SUCCESS.getValue());
        respVO.setTips("恭喜，申请成功！");
        return respVO;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long apply(Long userId, @Nullable Integer userType, SAVE reqVO) {
        DO aDo = getDetailByUserId(userId, userType);
        if (aDo == null) {
            return create(userId, userType, reqVO);
        }
        update(aDo.getId(), reqVO);
        return aDo.getId();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateAudit(AuditSaveDTO reqVO) {
        if (Objects.equals(reqVO.getAuditStatus(), AuditStatusEnum.WAIT.getValue())) {
            // 默认就是待审核状态，不允许从其他状态流转回来
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.AUDIT_STATUS_DENIED_FOR_WAIT);
        }
        // 校验存在
        DO aDo = validateExists(reqVO.getId());
        if (Objects.equals(aDo.getAuditStatus(), AuditStatusEnum.PASS.getValue())) {
            // 审核通过的，已经创建相关信息，为保证数据完整性，不允许再修改审核状态
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.AUDIT_STATUS_DENIED_FOR_PASS);
        }
        if (Objects.equals(aDo.getAuditStatus(), AuditStatusEnum.REJECT.getValue())
            && Objects.equals(reqVO.getAuditStatus(), AuditStatusEnum.REJECT.getValue())) {
            log.debug("[updateAudit] 已经是被驳回状态 {}", reqVO);
            if (!Objects.equals(aDo.getAuditNote(), reqVO.getAuditNote())) {
                aDo.setAuditNote(reqVO.getAuditNote());
                getInjectedMapper().updateById(aDo);
            }
            return;
        }
        // 更新：从待审核变成审核通过或被驳回、从被驳回变成审核通过
        aDo.setAuditStatus(reqVO.getAuditStatus());
        aDo.setAuditTime(LocalDateTime.now());
        boolean passed;
        if (Objects.equals(reqVO.getAuditStatus(), AuditStatusEnum.PASS.getValue())) {
            passed = true;
            aDo.setAuditNote("");
        } else {
            passed = false;
            aDo.setAuditNote(reqVO.getAuditNote());
        }
        buildAuditInfo(passed, aDo, reqVO);
        getInjectedMapper().updateById(aDo);
        TransactionUtils.afterCommit(() -> {
            onAuditAfter(passed, aDo);
            return null;
        });
    }

    @SuppressWarnings("unused")
    protected void buildAuditInfo(boolean passed, @Nonnull DO aDo, @Nonnull AuditSaveDTO reqVO) {

    }

    @SuppressWarnings("unused")
    protected void onAuditAfter(boolean passed, @Nonnull DO aDo) {

    }

    @Nonnull
    protected abstract DO buildSaveInfo(SAVE reqVO);

    @Nonnull
    protected abstract RESP buildApplyInfo(@Nonnull DO aDo);

    @Override
    public void updateWhenPaid(String tradeNo, Long userId, Long packageId) {

    }

}